package fun.mashuai.copy;

import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.file.FileAppender;
import cn.hutool.core.io.file.FileNameUtil;
import cn.hutool.core.io.file.FileReader;
import cn.hutool.core.thread.ExecutorBuilder;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.extra.compress.CompressUtil;
import cn.hutool.extra.compress.archiver.Archiver;
import org.apache.commons.compress.archivers.ArchiveStreamFactory;

import java.io.File;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;

/**
 * @author 马帅
 * @version 1.0
 * @description 栅格瓦片压缩tar包
 * @className RasterToTar.java
 * @date 2022/3/26 15:53
 */
public class RasterToTar {


    private static String dir = "D:\\dev-center\\map-data\\BingMap\\BingMap_SatelliteNone_World_zxy_1-14\\11";

    private static Integer tar_num = 100;

    private static Boolean xianchen = true;

    /**
     * 继续任务
     */
    private static Boolean task_continue = true;


    /**
     * 记录零字节瓦片
     */
    public static File recordZeroBytesFile(List<File> list, String zeroBytesFilePath){
        // 创建记录文件
        FileUtil.del(zeroBytesFilePath);
        File zeroFile = FileUtil.touch(zeroBytesFilePath);

        // 文件追加器，记录零字节瓦片
        FileAppender fileAppender = new FileAppender(zeroFile, 16, true);
        int zeroFiles = 0;

        for(File file : list){

            List<File> lists = FileUtil.loopFiles(file);
            for(File file1 : lists){
                String path = file1.getPath();
                //System.out.println("文件路径：" + path);
                if(file1.length() == 0){
                    zeroFiles += 1;
                    // 671\8435.png
                    String xy = path.replace(dir + File.separator, "");

                    //System.out.println("记录零字节图片：" + xy);
                    fileAppender.append(xy);
                }
            }
        }

        // 将剩余内容刷入文件
        fileAppender.flush();

        System.out.println("零字节文件记录完成，数量："+ zeroFiles);

        return zeroFile;

    }

    /**
     * 打tar包，过滤零字节文件
     *
     * @param tarFile tar文件
     * @param dirList 将要打入tar包的文件加路径
     * @param recordZeroBytesFileName 零字节记录文件
     */
    public static boolean toTar(File tarFile, List<File> dirList, String recordZeroBytesFileName, File taskRecordFile){
        System.out.println("打tar：" + tarFile.getPath());

        // 创建记录文件
        FileUtil.del(recordZeroBytesFileName);
        File zeroFile = FileUtil.touch(recordZeroBytesFileName);

        // 文件追加器，记录零字节瓦片
        FileAppender fileAppender = new FileAppender(zeroFile, 16, true);

        // 创建压缩包
        Archiver archiver = CompressUtil.createArchiver(CharsetUtil.CHARSET_UTF_8, ArchiveStreamFactory.TAR, tarFile);


        for(File file1 : dirList){
            try {
                /*if(countDirSize(file1) == 0){
                    FileUtil.touch(file1.getPath() + "\\空文件夹.txt");
                }*/

                // 将文件或文件夹打入tar包
                archiver.add(file1, file -> {
                    if("png".equals(FileNameUtil.extName(file)) && file.length() == 0){
                        String path = file.getPath();
                        String xy = path.replace(dir + File.separator, "");
                        fileAppender.append(xy);

                        return false;
                    }
                    return true;
                });

            } catch (Exception e){
                System.out.println("打tar失败：" + tarFile.getPath());
                System.out.println(e);
                return false;
            }
            // 将剩余内容刷入文件
            fileAppender.flush();
        }

        // 将剩余内容刷入文件
        fileAppender.flush();

        // 将记录文件加入压缩包
        archiver.add(zeroFile);
        // 关闭压缩
        archiver.finish().close();

        System.out.println("成功打包tar文件：" + tarFile.getPath());
        // 文件追加器，记录任务
        FileAppender taskFileAppender = new FileAppender(taskRecordFile, 16, true);
        // 记录
        fileAppender.append(tarFile.getPath().replace(dir + File.separator, ""));
        fileAppender.flush();

        return true;
    }


    public static long countDirSize(File file){
        long size = 0;
        List<File> list = FileUtil.loopFiles(file);
        for(File file1 : list){
            size += file1.length();
        }
        return size;
    }



    public static void main(String[] args) {
        long startTime = System.currentTimeMillis();

        if(args != null){
            if(args.length >= 1){
                dir = args[0];
            }
            if(args.length >= 2){
                tar_num = Integer.valueOf(args[1]);
            }
            if(args.length >= 3){
                xianchen = Boolean.valueOf(args[2]);
            }
            if(args.length >= 4){
                task_continue = Boolean.valueOf(args[3]);
            }

        } else {

            System.out.println("请输入参数：源目录与目标目录，空格隔开");

            return;
        }

        System.out.println("文件夹目录：" + dir);
        System.out.println("打包数量：" + tar_num);
        System.out.println("开始扫描...");

        // 获取源目录中所有文件绝对路径
        //List<File> list = FileUtil.loopFiles(dir);
        //System.out.println("共：" + list.size() + "个文件");

        // 查看所有x级文件夹
        List<File> dirList = Arrays.asList(FileUtil.ls(dir));
        List<File> dirList1 = new ArrayList<>();
        for(File file1 : dirList){
            if(FileUtil.isDirectory(file1)){
                dirList1.add(file1);
            }
        }

        // 分成10段
        List<List<File>> lists = ListUtil.splitAvg(dirList1, tar_num);
        System.out.println("一共有 " + lists.size() + " 段");

        String taskRecordPath = dir + File.separator + "taskRecord.txt";
        // 任务记录文件
        File taskRecordFile;
        if(task_continue){
            boolean exist_tasks = FileUtil.exist(taskRecordPath);
            if(!exist_tasks){
                System.out.println("不存在任务记录文件，请将继续任务参数设为false");
                return;
            }
            taskRecordFile = FileUtil.file(taskRecordPath);
        } else {
            taskRecordFile = FileUtil.touch(taskRecordPath);
        }


        // 文件读取
        FileReader fileReader = new FileReader(taskRecordFile);
        List<String> taskRecords =  fileReader.readLines();

        // 文件追加器，记录任务
        FileAppender fileAppender = new FileAppender(taskRecordFile, 16, true);

        ExecutorService executor = null;
        if(xianchen){
            // 线程
            executor = ExecutorBuilder.create()
                    // 初始线程数
                    .setCorePoolSize(10)
                    // 最大线程数
                    .setMaxPoolSize(20)
                    // 队列，有界等待队列，最大等待数是1024
                    .setWorkQueue(new ArrayBlockingQueue<>(1024))
                    .build();
        }


        // 计数
        int count = 0;
        int size = lists.size();
        for (int i = 0; i < size; i++) {
            List<File> list1 = lists.get(i);

            String name = list1.get(0).getName() + "-" + list1.get(list1.size()-1).getName();

            // 分配记录零字节文件名称
            String recordZeroBytesFileName = dir + File.separator + "zero-" + name + ".txt";

            // 分配tar包名称
            String tarName = "tar-" + name + ".tar";
            String tarPath = dir + File.separator + tarName;

            // 排除已打包任务
            if(taskRecords.contains(tarName)){
                continue;
            }

            if(xianchen){
                RecordAndTar recordAndTar = new RecordAndTar(list1, recordZeroBytesFileName, new File(tarPath), taskRecordFile);
                executor.execute(recordAndTar);
            }
            if(!xianchen){
                // 记录零字节瓦片数量(2555-3501)
                //File file1 = recordZeroBytesFile(list1, recordZeroBytesFileName);
                long thatTime = System.currentTimeMillis();

                boolean a = toTar(new File(tarPath), list1, recordZeroBytesFileName, taskRecordFile);

                //boolean a = toTar(new File(tarPath), list1, file1);
                if(!a){
                    break;
                } else {
                    // 记录
                    fileAppender.append(tarName);
                    fileAppender.flush();

                    System.out.println("零字节文件名称:" + recordZeroBytesFileName);
                    System.out.println("tar包路径:" + tarPath);

                    count = i + 1;
                    long thisTime = System.currentTimeMillis();
                    long useTime = thisTime - thatTime;
                    System.out.println("哈哈，共" + tar_num + "个包，已完成第" + count + "个");

                    System.out.println("用时：" + useTime/1000 + "秒");
                    System.out.println("预计剩余时间大约：" + (useTime * (tar_num - count))/1000/60 + "分钟");
                    // 换行
                    System.out.println(System.getProperty("line.separator"));
                }
            }
        }

        executor.shutdown();
        long endTime = System.currentTimeMillis();
        System.out.println("程序运行时间：" + (endTime - startTime)/1000/60 + "分钟");

    }



    public static class RecordAndTar implements Runnable{
        // 文件夹目录
        private List<File> list;
        // 零字节文件
        private String zeroBytesFilePath;
        // tar文件
        private File tarFile;
        // 记录任务文件
        private File taskRecordFile;

        public RecordAndTar(List<File> list, String zeroBytesFilePath, File tarFile, File taskRecordFile) {
            this.list = list;
            this.zeroBytesFilePath = zeroBytesFilePath;
            this.tarFile = tarFile;
            this.taskRecordFile = taskRecordFile;
        }

        @Override
        public void run() {

            long thisTime = System.currentTimeMillis();

            boolean a = toTar(tarFile, list, zeroBytesFilePath, taskRecordFile);

            if(a){
                long thatTime = System.currentTimeMillis();
                long useTime = thisTime - thatTime;

                System.out.println("用时：" + useTime/1000 + "秒");
            }


        }
    }




}
